/*
 * @(#)XfslPattern.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfdm.model.algorithm;

import xfuzzy.lang.*;
import xfuzzy.xfds.XfdsDataSet;;


/**
 * Clase que describe un conjunto de datos "fuzzificados"
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public class XfdmFuzzyDataSet {

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Descripci�n difusa de los atributos de entrada
	 */
	private Type[] attrib;
	
	/**
	 * Descripci�n difusa del atributo de clasificaci�n
	 */
	private Type classif;

	//----------------------------------------------------------------------------//
	//                            MIEMBROS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Grado de certeza de cada patr�n de datos
	 */
	public double[] certainty;
	
	/**
	 * Grado de pertenencia a cada MF de cada atributo en cada patr�n de datos
	 */
	public double[][][] input;
	
	/**
	 * Grado de pertenencia a cada MF del atributo de clasificaci�n para cada dato
	 */
	public double[][] output;

	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor basado en un conjunto de patrones "crisp"
	 */
	public XfdmFuzzyDataSet(XfdsDataSet dataset, Type[] attrib, Type classif) {
		this.attrib = attrib;
		this.classif = classif;
		
		int numpatterns = dataset.input.length;
		this.certainty = new double[numpatterns];
		for(int i=0; i<numpatterns; i++) this.certainty[i] = 1.0;
		
		this.input = new double[numpatterns][attrib.length][];
		for(int i=0; i<attrib.length; i++) {
			LinguisticLabel[] mf = attrib[i].getAllMembershipFunctions();
			for(int p = 0; p<numpatterns; p++) {
				input[p][i] = new double[mf.length];
				double crisp = dataset.input[p][i];
				for(int l = 0; l<mf.length; l++) {
					input[p][i][l] = mf[l].compute(crisp);
				}
			}
		}
		
		LinguisticLabel[] classifMF = classif.getAllMembershipFunctions();
		this.output = new double[numpatterns][classifMF.length];
		for(int p=0; p<numpatterns; p++) {
			double crisp = dataset.output[p][0];
			for(int l = 0; l<classifMF.length; l++) {
				output[p][l] = classifMF[l].compute(crisp);
			}
		}
	}

	/**
	 * Constructor basado en un conjunto de datos difusos del que hay que eliminar un atributo
	 */
	public XfdmFuzzyDataSet(XfdmFuzzyDataSet parent, int att) {
		Type[] parent_attr = parent.getAttrib();
		this.attrib = new Type[parent_attr.length-1];
		System.arraycopy(parent_attr, 0, attrib, 0, att);
		System.arraycopy(parent_attr, att+1, attrib, att, attrib.length-att);
		
		this.classif = parent.getClassif();

		int num_patterns = parent.input.length;

		this.certainty = new double[num_patterns];
		System.arraycopy(parent.certainty, 0, this.certainty, 0, num_patterns);

		this.input = new double[num_patterns][attrib.length][];
		for(int i=0; i<att; i++) {
			int numMFs = attrib[i].getAllMembershipFunctions().length;
			for(int p=0; p<num_patterns; p++) {
				this.input[p][i] = new double[numMFs];
				System.arraycopy(parent.input[p][i], 0, input[p][i], 0, numMFs);
			}
		}
		for(int i=att; i<attrib.length; i++) {
			int numMFs = attrib[i].getAllMembershipFunctions().length;
			for(int p=0; p<num_patterns; p++) {
				this.input[p][i] = new double[numMFs];
				System.arraycopy(parent.input[p][i+1], 0, input[p][i], 0, numMFs);
			}
		}
		
		int numMFs = classif.getAllMembershipFunctions().length;
		this.output = new double[num_patterns][numMFs];
		for(int p=0; p<num_patterns; p++) {
			this.output[p] = new double[numMFs];
			System.arraycopy(parent.output[p], 0, output[p], 0, numMFs);
		}
	}

	/**
	 * Constructor que hace una copia de otro conjunto de datos
	 */
	public XfdmFuzzyDataSet(XfdmFuzzyDataSet parent) {
		this.attrib = parent.getAttrib();
		this.classif = parent.getClassif();
		
		int num_patterns = parent.input.length;
		this.certainty = new double[num_patterns];
		System.arraycopy(parent.certainty, 0, this.certainty, 0, num_patterns);

		this.input = new double[num_patterns][attrib.length][];
		for(int i=0; i<attrib.length; i++) {
			int numMFs = attrib[i].getAllMembershipFunctions().length;
			for(int p=0; p<num_patterns; p++) {
				this.input[p][i] = new double[numMFs];
				System.arraycopy(parent.input[p][i], 0, input[p][i], 0, numMFs);
			}
		}
		
		int numMFs = classif.getAllMembershipFunctions().length;
		this.output = new double[num_patterns][numMFs];
		for(int p=0; p<num_patterns; p++) {
			this.output[p] = new double[numMFs];
			System.arraycopy(parent.output[p], 0, output[p], 0, numMFs);
		}
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Obtiene la descripci�n difusa de los atibutos de entrada
	 */
	public Type[] getAttrib() {
		return this.attrib;
	}
	
	/**
	 * Obtiene la descripci�n difusa del atributo de clasificaci�n
	 */
	public Type getClassif() {
		return this.classif;
	}
	
	/**
	 * Elimina los patrones cuyo grado de certeza no supere el umbral
	 * @param threshold
	 */
	public void prune(double threshold) {
		int count=0;
		for(int i=0; i<certainty.length; i++) if(certainty[i] > threshold) count++;
		
		double[] aux_c = new double[count];
		double[][][] aux_i = new double[count][][];
		double[][] aux_o = new double[count][];
		int j = 0;
		for(int i=0; i<certainty.length; i++) if(certainty[i] > threshold) {
			aux_c[j] = certainty[i];
			aux_i[j] = input[i];
			aux_o[j] = output[i];
			j++;
		}
		
		this.certainty = aux_c;
		this.input = aux_i;
		this.output = aux_o;
	}

	/**
	 * Obtiene un conjunto de datos donde s�lo se encuentran las instancias que
	 * superan un determinado umbral en la MF de salida indicada. Se usa para
	 * extraer las instancias positivas de esa MF de salida.
	 * @param omf
	 * @param threshold
	 * @return
	 */
	public XfdmFuzzyDataSet selectPositiveInstances(int omf, double threshold) {
		XfdmFuzzyDataSet p = new XfdmFuzzyDataSet(this);
		int count = 0;
		for(int i=0; i<this.certainty.length; i++) if(output[i][omf] > threshold) count++;
		
		int oMFs = classif.getAllMembershipFunctions().length;
		int[] numMFs = new int[this.attrib.length];
		for(int a=0; a<attrib.length; a++) numMFs[a] = attrib[a].getAllMembershipFunctions().length;
		
		p.certainty = new double[count];
		p.input = new double[count][this.attrib.length][];
		p.output = new double[count][oMFs];

		for(int i=0,j=0; i<this.certainty.length; i++) if(output[i][omf] > threshold) {
			p.certainty[j] = this.certainty[i];
			
			for(int a=0; a<attrib.length; a++) {
				p.input[j][a] = new double[numMFs[a]];
				System.arraycopy(this.input[i][a], 0, p.input[j][a], 0, numMFs[a]);
			}
			
			System.arraycopy(this.output[i], 0, p.output[j], 0, oMFs);

			j++;
		}
		
		return p;
	}
	
	/**
	 * Obtiene un conjunto de datos donde s�lo se encuentran las instancias que
	 * no superan un determinado umbral en la MF de salida indicada. Se usa para
	 * extraer las instancias negativas de esa MF de salida.
	 * @param omf
	 * @param threshold
	 * @return
	 */
	public XfdmFuzzyDataSet selectNegativeInstances(int omf, double threshold) {
		XfdmFuzzyDataSet p = new XfdmFuzzyDataSet(this);
		int count = 0;
		for(int i=0; i<this.certainty.length; i++) if(output[i][omf] <= threshold) count++;
		
		int oMFs = classif.getAllMembershipFunctions().length;
		int[] numMFs = new int[this.attrib.length];
		for(int a=0; a<attrib.length; a++) numMFs[a] = attrib[a].getAllMembershipFunctions().length;
		
		p.certainty = new double[count];
		p.input = new double[count][this.attrib.length][];
		p.output = new double[count][oMFs];

		for(int i=0,j=0; i<this.certainty.length; i++) if(output[i][omf] <= threshold) {
			p.certainty[j] = this.certainty[i];
			
			for(int a=0; a<attrib.length; a++) {
				p.input[j][a] = new double[numMFs[a]];
				System.arraycopy(this.input[i][a], 0, p.input[j][a], 0, numMFs[a]);
			}
			
			System.arraycopy(this.output[i], 0, p.output[j], 0, oMFs);

			j++;
		}
		
		return p;
	}
	
	/**
	 * Eliminas las instancias cubiertas por un antecedente
	 * @param antecedent Representaci�n del antecedente. 
	 * @param threshold
	 * @param and
	 */
	public void pruneInstances(int[][] complex, double threshold, Binary and) {
		int count = 0;
		int num_patterns = this.certainty.length;
		for(int p=0; p<num_patterns; p++) {
			double deg = this.certainty[p];
			for(int i=0; i<complex.length; i++) {
				deg = and.compute(deg,this.input[p][complex[i][0]][complex[i][1]]);
			}
			if(deg<=threshold) count++;
		}
		
		int new_size = count;
		double[] aux_c = new double[new_size];
		double[][][] aux_i = new double[new_size][][];
		double[][] aux_o = new double[new_size][];
		int np=0;
		for(int p=0; p<num_patterns; p++) {
			double deg = this.certainty[p];
			for(int i=0; i<complex.length; i++) {
				deg = and.compute(deg,this.input[p][complex[i][0]][complex[i][1]]);
			}
			if(deg<=threshold) {
				aux_c[np] = this.certainty[p];
				aux_i[np] = this.input[p];
				aux_o[np] = this.output[p];
				np++;
			}
		}	
		
		this.certainty = aux_c;
		this.input = aux_i;
		this.output = aux_o;
	}
	
	/**
	 * Comprueba si todas las instancias tienen grado de certeza cero
	 */
	public boolean isZeroCertainly() {
		int i = 0;
		boolean allzeros = true;
		while((i<this.certainty.length) && (allzeros)) {
			if(this.certainty[i] != 0) {
				allzeros = false;
			}
			i++;
		}
		return allzeros;
	}
	
}